//! RK3588 GPIO Driver Usage Example for Orange Pi 5 Plus
//!
//! This example demonstrates how to use the GPIO driver for basic operations
//! including reading, writing, and interrupt handling.

#![no_std]
#![no_main]

use axplat_aarch64_peripherals::rk3588_gpio::{
    init_gpio, init_all, GpioPin, GpioPinHandle, GpioDirection, 
    GpioTrigger, read, write, toggle, set_direction, set_interrupt,
};
use axplat::mem::VirtAddr;

// GPIO base addresses for RK3588
const GPIO0_BASE: usize = 0xFD8A0000;
const GPIO1_BASE: usize = 0xFEC20000;
const GPIO2_BASE: usize = 0xFEC30000;
const GPIO3_BASE: usize = 0xFEC40000;
const GPIO4_BASE: usize = 0xFEC50000;

/// Example 1: Initialize all GPIO controllers
fn example_init_all() {
    let gpio_bases = [
        VirtAddr::from(GPIO0_BASE),
        VirtAddr::from(GPIO1_BASE),
        VirtAddr::from(GPIO2_BASE),
        VirtAddr::from(GPIO3_BASE),
        VirtAddr::from(GPIO4_BASE),
    ];
    init_all(&gpio_bases);
}

/// Example 2: Initialize a single GPIO controller
fn example_init_single() {
    // Initialize only GPIO4
    init_gpio(4, VirtAddr::from(GPIO4_BASE));
}

/// Example 3: Basic GPIO output - Blink LED
fn example_blink_led() {
    // Assume LED is connected to GPIO4_B0 (pin 8 of GPIO4)
    let led_pin = GpioPin::GPIO4_B0;
    
    // Set as output
    set_direction(led_pin, GpioDirection::Output);
    
    // Blink loop
    for _ in 0..10 {
        write(led_pin, true);   // LED on
        // delay(500ms)
        write(led_pin, false);  // LED off
        // delay(500ms)
    }
}

/// Example 4: GPIO input - Read button state
fn example_read_button() {
    // Assume button is connected to GPIO3_A0 (pin 0 of GPIO3)
    let button_pin = GpioPin::GPIO3_A0;
    
    // Set as input
    set_direction(button_pin, GpioDirection::Input);
    
    // Read button state
    let is_pressed = read(button_pin);
    if is_pressed {
        // Button is pressed (assuming active high)
        // Do something...
    }
}

/// Example 5: Using GpioPinHandle for safer API
fn example_gpio_handle() {
    // Create output pin handle
    let mut led = GpioPinHandle::new(GpioPin::GPIO4_B0, GpioDirection::Output);
    
    // Use high-level API
    led.set_high();
    led.set_low();
    led.toggle();
    
    // Create input pin handle
    let button = GpioPinHandle::new(GpioPin::GPIO3_A0, GpioDirection::Input);
    if button.is_high() {
        led.toggle();
    }
}

/// Example 6: GPIO interrupt handling
fn example_gpio_interrupt() {
    // Configure button press interrupt (rising edge)
    let button_pin = GpioPin::GPIO3_A0;
    
    // Set as input
    set_direction(button_pin, GpioDirection::Input);
    
    // Enable rising edge interrupt
    set_interrupt(button_pin, GpioTrigger::RisingEdge);
    
    // In your interrupt handler, call:
    // use axplat_aarch64_peripherals::rk3588_gpio::irq_handler;
    // irq_handler(3); // For GPIO3
}

/// Example 7: Advanced - Controlling multiple pins
fn example_multiple_pins() {
    // Initialize array of LED pins
    let led_pins = [
        GpioPin::GPIO4_A0,
        GpioPin::GPIO4_A1,
        GpioPin::GPIO4_A2,
        GpioPin::GPIO4_A3,
    ];
    
    // Configure all as outputs
    for pin in &led_pins {
        set_direction(*pin, GpioDirection::Output);
    }
    
    // Create a running light effect
    for _ in 0..5 {
        for pin in &led_pins {
            write(*pin, true);
            // delay(100ms)
            write(*pin, false);
        }
    }
}

/// Example 8: Using custom GPIO pins
fn example_custom_pin() {
    // GPIO1_C5 = GPIO1 Port C Pin 5 = GPIO1 Pin 21 (16 + 5)
    let custom_pin = GpioPin::from_port_pin(1, 2, 5); // bank=1, port=2(C), pin=5
    
    set_direction(custom_pin, GpioDirection::Output);
    write(custom_pin, true);
}

/// Example 9: Complete application - Button controlled LED
fn example_button_controlled_led() {
    // Setup
    let led = GpioPin::GPIO4_B0;
    let button = GpioPin::GPIO3_A0;
    
    set_direction(led, GpioDirection::Output);
    set_direction(button, GpioDirection::Input);
    
    // Main loop
    loop {
        if read(button) {
            write(led, true);  // Button pressed, turn on LED
        } else {
            write(led, false); // Button released, turn off LED
        }
        // Small delay to debounce
    }
}

/// Example 10: Interrupt-driven button with toggle
fn example_interrupt_driven_toggle() {
    let led = GpioPin::GPIO4_B0;
    let button = GpioPin::GPIO3_A0;
    
    set_direction(led, GpioDirection::Output);
    set_direction(button, GpioDirection::Input);
    
    // Enable interrupt on button press (rising edge)
    set_interrupt(button, GpioTrigger::RisingEdge);
    
    // In your interrupt handler:
    // fn gpio3_irq_handler() {
    //     use axplat_aarch64_peripherals::rk3588_gpio::{irq_handler, get_interrupt_status, toggle};
    //     let status = get_interrupt_status(3);
    //     if (status & (1 << 0)) != 0 { // Check if pin 0 triggered
    //         toggle(GpioPin::GPIO4_B0); // Toggle LED
    //     }
    //     irq_handler(3); // Clear interrupt
    // }
}

// Common GPIO mappings for Orange Pi 5 Plus (reference)
// Note: These are examples - actual pin assignments depend on your board configuration
//
// 40-Pin Header GPIO Mappings:
// Pin 3  (SDA): GPIO3_A6
// Pin 5  (SCL): GPIO3_A5
// Pin 7:        GPIO0_B1
// Pin 11:       GPIO0_C6
// Pin 13:       GPIO0_C7
// Pin 15:       GPIO0_D0
// Pin 19 (MOSI):GPIO4_A6
// Pin 21 (MISO):GPIO4_A5
// Pin 23 (SCLK):GPIO4_A4
// Pin 24 (CS):  GPIO4_B5
// Pin 29:       GPIO1_A0
// Pin 31:       GPIO1_A1
// Pin 33:       GPIO1_A2
// Pin 35:       GPIO1_A4
// Pin 37:       GPIO1_A3

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_gpio_pin_creation() {
        let pin = GpioPin::new(4, 8);
        assert_eq!(pin.bank, 4);
        assert_eq!(pin.pin, 8);
        
        let pin2 = GpioPin::from_port_pin(1, 2, 5);
        assert_eq!(pin2.bank, 1);
        assert_eq!(pin2.pin, 21); // Port C (2) * 8 + Pin 5 = 21
    }
}

